﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Xml.Linq;
using IridiumDB.Model;
using System.Globalization;
using IridiumFlares;

namespace IridiumDB.Repository
{
    public class FlareRepository : BaseXmlRepository
    {
        private String _datafile = "Flares.xml";
        private const String _xmlRoot = "Flares";
        private XDocument _doc;

        private const String ElementName_ID = "ID";
        private const String ElementName_Time = "Time";
        private const String ElementName_Azimuth = "Azimuth";
        private const String ElementName_Altitude = "Altitude";
        private const String ElementName_Intensity = "Intensity";
        private const String ElementName_Location_Ref = "Location_Ref";
        private const String ElementName_Satellite_No = "Satellite_No";

        public FlareRepository()
        {
            _datafile = this.SetUpDataFile(_datafile, _xmlRoot, "1.1", true);
            _doc = XDocument.Load(_datafile);
        }

        public void Create(Flare item)
        {
            //Manipulate
            if (item.ID == Guid.Empty)
                item.ID = Guid.NewGuid();
            XElement xml = ItemToXElement(item);
            _doc.Element(_xmlRoot).Add(xml);

            //Save
            _doc.Save(_datafile);
        }

        public void Update(Flare item)
        {
            XElement oldVersion = (from entry in _doc.Descendants("Flare")
                                  where GetGuid(entry, ElementName_ID).Equals(item.ID)
                                  select entry).SingleOrDefault();
            oldVersion.Remove();
            Create(item);
        }

        public bool Exists(Flare item)
        {
            var match = (from flare in _doc.Descendants("Flare")
                         where flare.Element(ElementName_Time) != null && flare.Element(ElementName_Time).Value == new XElement(ElementName_Time, item.Time).Value
                         || IsUpdate(flare, item)
                         select flare).SingleOrDefault();

            return match != null;
        }

        private bool IsUpdate(XElement element, Flare newItem)
        {
            return IsUpdate(XElementToItem(element), newItem);
        }

        public bool IsUpdate(Flare existing, Flare newItem)
        {
            //Condition 1: Make sure it is the same satellite
            bool sameSatellite = existing.SatelliteNo == newItem.SatelliteNo;
            if (sameSatellite == false) return false;

            //Condition 2: Make sure time is not exactly the same
            if (existing.Time == newItem.Time) return false;

            //Condition 3: Make sure the different times vary for nothing but a few seconds
            var timeDiff = existing.Time - newItem.Time;
            TimeSpan almostNoDifference = TimeSpan.FromSeconds(10);
            bool almostSameTime = timeDiff.Duration() <= almostNoDifference.Duration();

            return sameSatellite && almostSameTime;
        }

        public List<Flare> FindAllUpcoming()
        {
            return (from flare in _doc.Descendants("Flare")
                   where DateTime.Parse(flare.Element(ElementName_Time).Value) >= DateTime.Now
                   orderby flare.Element(ElementName_Time).Value
                   select XElementToItem(flare)).ToList();
        }

        public List<Flare> FindAllUpcomingForLocationId(Guid locationId)
        {
            return (from flare in _doc.Descendants("Flare")
                    where DateTime.Parse(flare.Element(ElementName_Time).Value) >= DateTime.Now
                    && flare.Element(ElementName_Location_Ref).Value == locationId.ToString()
                    orderby flare.Element(ElementName_Time).Value
                    select XElementToItem(flare)).ToList();
        }

        private XElement ItemToXElement(Flare item)
        {
            XElement xml = new XElement("Flare",
                new XElement(ElementName_ID, item.ID),
                new XElement(ElementName_Time, item.Time),
                new XElement(ElementName_Azimuth, item.Azimuth),
                new XElement(ElementName_Altitude, item.Altitude),
                new XElement(ElementName_Intensity, item.Intensity),
                new XElement(ElementName_Location_Ref, item.Location_Ref),
                new XElement(ElementName_Satellite_No, item.SatelliteNo)
            );
            return xml;
        }

        private Flare XElementToItem(XElement element)
        {
            Guid ID = GetGuid(element, ElementName_ID);
            int Altitude = GetInt(element, ElementName_Altitude);
            int Azimuth = GetInt(element, ElementName_Azimuth);
            int Intensity = GetInt(element, ElementName_Intensity);
            DateTime Time = GetDateTime(element, ElementName_Time);
            Guid LocationRef = GetGuid(element, ElementName_Location_Ref);
            int satelliteNo = GetInt(element, ElementName_Satellite_No);

            return new Flare
            {
                ID = ID,
                Altitude = Altitude,
                Azimuth = Azimuth,
                Intensity = Intensity,
                Time = Time,
                Location_Ref = LocationRef,
                SatelliteNo = satelliteNo
            };
        }
    }
}